package com.globalwave.pos.inventory.service;

import java.util.List;
import java.util.Map;

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;


import com.globalwave.common.C;
import com.globalwave.common.U;
import com.globalwave.common.exception.BusinessException;
import com.globalwave.pos.inventory.entity.InventoryBalance;
import com.globalwave.pos.inventory.entity.InventoryPeriod;
import com.globalwave.pos.inventory.entity.InventorySO;
import com.globalwave.pos.inventory.entity.InventoryTransaction;
import com.globalwave.system.entity.SessionUser;
import com.globalwave.system.entity.User;

@Service("inventoryBalanceBO")
@Scope("prototype")
@Transactional
public class InventoryBalanceBO extends InventoryBaseBO {

	private InventoryBalance balance ;
	public InventoryBalanceBO() {
		
	}
	
	public InventoryBalanceBO(String warehouseCode) {
		super.warehouseCode = warehouseCode ;
	}
	
	
	private InventoryTransactionBO transactionBO ;
	private InventoryPeriodBO inventoryPeriodBO ;
	
	public InventoryBalance get(Long stockItemNumber) throws Exception {
		
		if (balance != null && stockItemNumber.equals(balance.getStock_item_number())) {
			return balance ;
		}
				
		InventoryBalance balanceSo = new InventoryBalance() ;
		balanceSo.setWarehouse_code(warehouseCode) ;
		balanceSo.setStock_item_number(stockItemNumber) ;
		
		//return (InventoryBalance)accessor.findName("inventory:getInventoryOnHandBalance", so);
		balance = (InventoryBalance)jdbcDao.find(balanceSo) ;
		
		return balance;
	}

	public InventoryBalance update(InventoryTransaction transaction) throws Exception {
		
		warehouseCode = transaction.getWarehouse_code() ;
		Long stockItemNumber = transaction.getStock_item_number();
		
		if (InventoryTransaction.TRANS_CODE_SAFE_QUANTITY_UPDATE.equals(
				transaction.getTransaction_code())) {
			
			InventoryBalance balance = get(stockItemNumber) ;
			
			balance.setSafe_quantity(transaction.getOn_hand_quantity()) ;
			balance.addInclusions("safe_quantity") ;
			
			jdbcDao.update(balance) ;
			
		} else {// just for on-hand adjust!
			User user = SessionUser.get().getUser() ;
			
			transaction.setMortgaged_quantity(C.ZERO_INTEGER) ;
			transaction.setUnsalable_quantity(C.ZERO_INTEGER) ;
			
			transaction.setTransaction_date(U.date2int(U.currentDate())) ;
			transaction.setStatus_(C.STATUS_ACTIVE) ;
			transaction.setRegion_code(user.getRegion_code()) ;
			
			jdbcDao.insert(transaction) ;
		}
		
		
		return getCurrent(warehouseCode, stockItemNumber) ;
	}
	
	/**
	 * 获取当前的库存状态/数量（已经计划transaction数量）
	 * 
	 * @param stockItemNumber
	 * @return
	 * @throws Exception
	 */
	public InventoryBalance getCurrent(String warehouseCode, Long stockItemNumber) throws Exception {
		
		this.warehouseCode = warehouseCode ;
		
		InventoryBalance result = get(stockItemNumber) ;
		
		InventorySO so = new InventorySO() ;
		so.setWarehouse_code(warehouseCode) ;
		so.setStock_item_number(stockItemNumber) ;
		
		List<InventoryTransaction> trans = 
			this.getTransactionBO().sumQuantityFormTransation(so) ;
		
		if (trans.size() > 0) {
			InventoryTransaction tran = trans.get(0) ;
			result.setOn_hand_quantity(tran.getOn_hand_quantity() + result.getOn_hand_quantity()) ;
			result.setMortgaged_quantity(tran.getMortgaged_quantity() + result.getMortgaged_quantity()) ;
			result.setUnsalable_quantity(tran.getUnsalable_quantity() + result.getUnsalable_quantity()) ;
		}
		
		return result ;
	}

	/**
	 * 
	 * 
	 * @param stockItemNum 库存编号
	 * @param salingAmount 买出数量
	 * @throws Exception
	 */
	public void validateSalable(Long stockItemNumber, int salingAmount, Long saleDateNow) throws Exception {

		
		this.get(stockItemNumber) ;
		
		if (balance != null) {
			
			balance.setDateNow(saleDateNow) ;
			if (InventoryBalance.INVENTORY_LOCK_UNSALABLE.equals(balance.getSale_status())) {
			    throw new BusinessException("err.inv.stock.unsalable", stockItemNumber);
			}
		}
		
		
		Integer salableAmount = getSalableQuantity(stockItemNumber) ;
		if (salableAmount < salingAmount) {
			throw new BusinessException("err.inv.more.than.salable", salingAmount, salableAmount);
		}
	}
	
	/**
	 * 取得某一产品在指定仓库的可销售数量
	 * 
	 * @param warehouse 仓库编号
	 * @param itemNum 产品编号
	 * 
	 * @return 产品的可销售数量
	 * @throws Exception
	 */
	public Integer getSalableQuantity(Long stockItemNum) throws Exception {
		// 取得Balance中的onhand
		InventoryBalance balance = get(stockItemNum) ;
		// 取得transation(除去shipment)中的总和 sum(quantity)
		Map<Long,Integer> quantitys = getTransactionBO().getAvailableBalance(stockItemNum) ;
		// onhand + sum(quantity) - safe = salable
		Integer quantity = quantitys.get(stockItemNum) ;
		
		if (quantity == null) {
			quantity = new Integer(0) ;
		}
		
		if (balance == null) {
			return quantity;			
		} else {
			return balance.getOn_hand_quantity() - balance.getMortgaged_quantity() - balance.getSafe_quantity() + quantity ;
		}
	}

	final static private int BATCH_SIZE = 3000 ;
	public void archiveCurrentPeriod() throws Exception {
		archiveCurrentPeriod(null) ;
	}
	/**
	 * 把InventoryTemporaryTransaction进行归并，当InventoryTemporaryTransaction写回历史
	 * 
	 */
	public void archiveCurrentPeriod(InventoryPeriod currentPeriod) throws Exception {
		
		InventoryPeriod current = currentPeriod == null? getInventoryPeriodBO().current():currentPeriod ;
		
		InventorySO inventorySO = 
			(InventorySO)jdbcDao.findName("inventorySQLs:maxInventoryTransactionId", current, InventorySO.class) ;
		    		/*(InventorySO)jdbcDao.query(
		    				inventorySQLs.get("maxInventoryTransactionId"), 
		    				InventorySO.class, 
		    				current, 
		    				null).get(0) ;*/
		
		if (inventorySO == null || inventorySO.getMin() == null) {// no transaction need to archive 
			return ;
		}

		inventorySO.setStartDate(current.getDate_from()) ;
		inventorySO.setEndDate(current.getDate_to());
		
		long min = inventorySO.getMin() ;
		long max = inventorySO.getMax() ;
		//long total = inventorySO.getTotal() ;
		long endId = max ;
		
		InventoryTransactionBO transBo = getTransactionBO() ;
		
		while (min <= endId) {
			if (min + BATCH_SIZE < endId) {// caculate end at
				inventorySO.setMax(min + BATCH_SIZE) ;
			} else {
				inventorySO.setMax(endId) ;
			}
			
			long totalResult = ((InventorySO)jdbcDao.findName("inventorySQLs:countTempTransactions", inventorySO, InventorySO.class)).getTotal() ;
			
			
			initBalanceFromTransaction(inventorySO) ;
			sumTransactionsToBalance(inventorySO) ;
			
			transBo.moveTransactionsToHistory(inventorySO) ;
			int deleteResult = transBo.deleteTempTransactions(inventorySO) ;

			if (totalResult != deleteResult) {
				throw new Exception("record count not right!") ;
			}
			
			min = inventorySO.getMax() + 1 ;// next start at
			inventorySO.setMin(min) ;
		}
	}

	public void archiveAllPeriod() throws Exception {
		//InventorySO inventorySO = (InventorySO)accessor.findName("inventory:maxInventoryTransactionId", null) ;
		InventorySO inventorySO = (InventorySO)jdbcDao.findName("inventorySQLs:maxInventoryTransactionId", null, InventorySO.class) ;
		sumTransactionsToPeriodBalance(inventorySO) ;
	}
	
	/* *
	 * 把InventoryTemporaryTransaction进行归并，当InventoryTemporaryTransaction写回历史
	 * 
	 
	private void archive(String warehouse, Long stockItemNum) {
		// sum onhand balance
		// sum morgige balance
		// add sum to balance_master
		// move transation to history
		// delete temp transation
	}*/

	
	
	
	private void initBalanceFromTransaction(InventorySO inventorySO) throws Exception {
		jdbcDao.executeName("inventorySQLs:initBalanceFromTransaction", inventorySO) ;

	}
	
	private void sumTransactionsToBalance(InventorySO inventorySO) throws Exception {
		jdbcDao.executeName("inventorySQLs:sumTransactionsToBalance", inventorySO) ;
	}
	
	private void sumTransactionsToPeriodBalance(InventorySO inventorySO) throws Exception {
		jdbcDao.executeName("inventorySQLs:sumTransactionsToPeriodBalance", inventorySO) ;
	}

	public void clear() {
		this.balance = null ;
	}
	
	public InventoryTransactionBO getTransactionBO() {
		
		if (transactionBO == null) {
			transactionBO = new InventoryTransactionBO(warehouseCode) ;
			transactionBO.setJdbcDao(this.jdbcDao) ;
		}
		
		return transactionBO;
	}

	public void setTemporaryTransactionBO(InventoryTransactionBO temporaryTransactionBO) {
		this.transactionBO = temporaryTransactionBO;
	}
	
	

	
	public InventoryPeriodBO getInventoryPeriodBO() {
		if (inventoryPeriodBO == null) {
			inventoryPeriodBO = new InventoryPeriodBO() ;
			inventoryPeriodBO.setJdbcDao(this.jdbcDao) ;
		}
		return inventoryPeriodBO ;
	}

	public void setInventoryPeriodBO(InventoryPeriodBO inventoryPeriodDefinitionBO) {
		this.inventoryPeriodBO = inventoryPeriodDefinitionBO;
	}
}
